package com.efamily.ewallet.client.balance.widget;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

import com.efamily.ewallet.client.account.bean.AccountBean;
import com.efamily.ewallet.client.balance.bean.BalanceBean;
import com.efamily.ewallet.client.balance.bean.BalanceSearchConfig;
import com.efamily.ewallet.client.balance.service.BalanceServiceAsync;
import com.efamily.ewallet.client.common.DifferentItemsSummaryRenderer;
import com.efamily.ewallet.client.common.SelectedBalanceItemEvent;
import com.efamily.ewallet.client.common.consts.DateHierarhy;
import com.efamily.ewallet.client.exception.DayConvertException;
import com.efamily.ewallet.client.exception.MonthConvertException;
import com.efamily.ewallet.client.utils.Utils;
import com.extjs.gxt.ui.client.data.BasePagingLoader;
import com.extjs.gxt.ui.client.data.ModelData;
import com.extjs.gxt.ui.client.data.PagingLoadConfig;
import com.extjs.gxt.ui.client.data.PagingLoadResult;
import com.extjs.gxt.ui.client.data.PagingLoader;
import com.extjs.gxt.ui.client.data.RpcProxy;
import com.extjs.gxt.ui.client.event.MenuEvent;
import com.extjs.gxt.ui.client.event.SelectionChangedEvent;
import com.extjs.gxt.ui.client.event.SelectionChangedListener;
import com.extjs.gxt.ui.client.event.SelectionListener;
import com.extjs.gxt.ui.client.mvc.Dispatcher;
import com.extjs.gxt.ui.client.store.GroupingStore;
import com.extjs.gxt.ui.client.store.ListStore;
import com.extjs.gxt.ui.client.widget.Composite;
import com.extjs.gxt.ui.client.widget.ContentPanel;
import com.extjs.gxt.ui.client.widget.button.Button;
import com.extjs.gxt.ui.client.widget.grid.ColumnConfig;
import com.extjs.gxt.ui.client.widget.grid.ColumnData;
import com.extjs.gxt.ui.client.widget.grid.ColumnModel;
import com.extjs.gxt.ui.client.widget.grid.Grid;
import com.extjs.gxt.ui.client.widget.grid.GridCellRenderer;
import com.extjs.gxt.ui.client.widget.grid.GridGroupRenderer;
import com.extjs.gxt.ui.client.widget.grid.GroupColumnData;
import com.extjs.gxt.ui.client.widget.grid.GroupSummaryView;
import com.extjs.gxt.ui.client.widget.grid.SummaryColumnConfig;
import com.extjs.gxt.ui.client.widget.grid.SummaryRenderer;
import com.extjs.gxt.ui.client.widget.grid.SummaryType;
import com.extjs.gxt.ui.client.widget.layout.FitLayout;
import com.extjs.gxt.ui.client.widget.menu.CheckMenuItem;
import com.extjs.gxt.ui.client.widget.menu.Menu;
import com.extjs.gxt.ui.client.widget.toolbar.PagingToolBar;
import com.extjs.gxt.ui.client.widget.toolbar.ToolBar;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.user.client.rpc.AsyncCallback;

public class BalanceGridWidget extends Composite {
	private ContentPanel contentPanel = new ContentPanel();
	private Grid<BalanceBean> grid;
	private BalanceServiceAsync balanceService = BalanceServiceAsync.instance;
	private BalanceSearchConfig searchConfig = new BalanceSearchConfig();
	private AccountBean accountBean = new AccountBean();

	public BalanceGridWidget(AccountBean accountBean) {
		this.accountBean = accountBean;
		buildGrid();
		addToolbar();
		contentPanel.setHeaderVisible(false);
		contentPanel.setLayout(new FitLayout());
		initComponent(contentPanel);
	}

	private void buildGrid() {
		RpcProxy<PagingLoadResult<BalanceBean>> proxy = new RpcProxy<PagingLoadResult<BalanceBean>>() {
			@Override
			protected void load(Object loadConfig,
					AsyncCallback<PagingLoadResult<BalanceBean>> callback) {
				balanceService.getBalanceBeans(accountBean, searchConfig,
						(PagingLoadConfig) loadConfig, callback);
			}
		};

		PagingLoader<PagingLoadResult<BalanceBean>> loader = new BasePagingLoader<PagingLoadResult<BalanceBean>>(
				proxy);
		loader.setRemoteSort(true);
		PagingToolBar pagingToolBar = new PagingToolBar(40);
		pagingToolBar.bind(loader);
		contentPanel.setBottomComponent(pagingToolBar);
		GroupingStore<BalanceBean> store = new GroupingStore<BalanceBean>(
				loader);
		store.groupBy("date");

		final ColumnModel columnModel = buildColumnModel(DateHierarhy.DAY);

		GroupSummaryView view = new GroupSummaryView();
		view.setShowGroupedColumn(true);
		view.setForceFit(true);
		view.setGroupRenderer(new GridGroupRenderer() {
			public String render(GroupColumnData data) {
				String f = columnModel.getColumnById(data.field).getHeader();
				String l = data.models.size() == 1 ? "Item" : "Items";
				return f + ": " + data.group + " (" + data.models.size() + " "
						+ l + ")";
			}
		});

		grid = new Grid<BalanceBean>(store, columnModel);
		grid.getSelectionModel().addSelectionChangedListener(
				new SelectionChangedListener<BalanceBean>() {
					@Override
					public void selectionChanged(
							SelectionChangedEvent<BalanceBean> se) {
						List<BalanceBean> selectedBalanceItems = se
								.getSelection();
						List<Date> balanceDates = new ArrayList<Date>();
						for (BalanceBean balanceItem : selectedBalanceItems) {
							balanceDates.add(balanceItem.getDate());
						}
						SelectedBalanceItemEvent event = new SelectedBalanceItemEvent(
								searchConfig.getDetailedBy(), balanceDates);
						Dispatcher.get().fireEvent(event.getType(), event);
					}
				});
		grid.setView(view);
		grid.setBorders(true);
		grid.setAutoExpandColumn("description");
		grid.setMinColumnWidth(40);
		grid.setStripeRows(true);
		contentPanel.add(grid);
	}

	private ColumnModel buildColumnModel(DateHierarhy detailedBy) {
		searchConfig.setDetailedBy(detailedBy);
		SummaryColumnConfig<Integer> id = new SummaryColumnConfig<Integer>(
				"id", "Identifier", 55);
		id.setHidden(true);
		id.setGroupable(false);
		SummaryColumnConfig<Integer> weekDay = new SummaryColumnConfig<Integer>(
				"date", "Day", 80);
		// weekDay.setHidden(true);
		weekDay.setRenderer(new GridCellRenderer<ModelData>() {
			@SuppressWarnings("deprecation")
			public Object render(ModelData model, String property,
					ColumnData config, int rowIndex, int colIndex,
					ListStore<ModelData> store, Grid<ModelData> grid) {
				try {
					Date date = (Date) model.get(property);
					String day = Utils.intToDayName(date.getDay());
					return day;
				} catch (DayConvertException e) {
					e.printStackTrace();
					return null;
				}
			}

		});
		weekDay.setSummaryType(new DifferentItemsSummaryRenderer());
		weekDay.setSummaryRenderer(new SummaryRenderer() {
			public String render(Number value, Map<String, Number> data) {
				return value.intValue() > 1 ? "(In" + value.intValue()
						+ " Days)" : "(In 1 Day)";
			}
		});
		SummaryColumnConfig<Integer> monthDay = new SummaryColumnConfig<Integer>(
				"date", "Day Number", 80);
		monthDay.setRenderer(new GridCellRenderer<ModelData>() {
			@SuppressWarnings("deprecation")
			public Object render(ModelData model, String property,
					ColumnData config, int rowIndex, int colIndex,
					ListStore<ModelData> store, Grid<ModelData> grid) {
				Date date = (Date) model.get(property);
				int dayNumber = date.getDate();
				return dayNumber;
			}

		});
		monthDay.setSummaryType(new DifferentItemsSummaryRenderer());
		monthDay.setSummaryRenderer(new SummaryRenderer() {
			public String render(Number value, Map<String, Number> data) {
				return value.intValue() > 1 ? "(In " + value.intValue()
						+ " Days of month)" : "(In 1 Day of month)";
			}
		});
		SummaryColumnConfig<Integer> month = new SummaryColumnConfig<Integer>(
				"date", "Month", 80);
		// month.setHidden(true);
		month.setRenderer(new GridCellRenderer<ModelData>() {
			@SuppressWarnings("deprecation")
			public Object render(ModelData model, String property,
					ColumnData config, int rowIndex, int colIndex,
					ListStore<ModelData> store, Grid<ModelData> grid) {
				try {
					Date date = (Date) model.get(property);
					String monthName = Utils.intToMonthName(date.getMonth());
					return monthName;
				} catch (MonthConvertException e) {
					e.printStackTrace();
					return null;
				}

			}

		});
		month.setSummaryType(new DifferentItemsSummaryRenderer());
		month.setSummaryRenderer(new SummaryRenderer() {
			public String render(Number value, Map<String, Number> data) {
				return value.intValue() > 1 ? "(In " + value.intValue()
						+ " Monthes)" : "(In 1 Month)";
			}
		});
		SummaryColumnConfig<Integer> year = new SummaryColumnConfig<Integer>(
				"date", "Year", 80);
		// year.setHidden(true);
		year.setRenderer(new GridCellRenderer<ModelData>() {
			@SuppressWarnings("deprecation")
			public Object render(ModelData model, String property,
					ColumnData config, int rowIndex, int colIndex,
					ListStore<ModelData> store, Grid<ModelData> grid) {
				Date date = (Date) model.get(property);
				int yearNumber = 1900 + date.getYear();
				return yearNumber;
			}
		});
		year.setSummaryType(new DifferentItemsSummaryRenderer());
		year.setSummaryRenderer(new SummaryRenderer() {
			public String render(Number value, Map<String, Number> data) {
				return value.intValue() > 1 ? "(In " + value.intValue()
						+ " Years)" : "(In 1 Year)";
			}
		});
		SummaryColumnConfig<Double> expensesCount = new SummaryColumnConfig<Double>(
				"expensesCount", "Expenses Count", 80);
		// value.setHidden(true);
		expensesCount.setSummaryType(SummaryType.SUM);

		SummaryColumnConfig<Double> expensesAmount = new SummaryColumnConfig<Double>(
				"expensesAmount", "Expenses Amount", 80);
		// value.setHidden(true);
		expensesAmount.setSummaryType(SummaryType.SUM);

		SummaryColumnConfig<Double> profitsCount = new SummaryColumnConfig<Double>(
				"profitsCount", "Profits Count", 80);
		// value.setHidden(true);
		expensesCount.setSummaryType(SummaryType.SUM);
		SummaryColumnConfig<Double> profitsAmount = new SummaryColumnConfig<Double>(
				"profitsAmount", "Profits Amount", 80);
		// value.setHidden(true);
		profitsAmount.setSummaryType(SummaryType.SUM);

		SummaryColumnConfig<Integer> date = new SummaryColumnConfig<Integer>(
				"date", "Date", 80);
		// profitDate.setHidden(true);
		date.setDateTimeFormat(DateTimeFormat.getFormat("dd/MM/y"));
		date.setSummaryType(new DifferentItemsSummaryRenderer());
		date.setSummaryRenderer(new SummaryRenderer() {
			public String render(Number value, Map<String, Number> data) {
				return value.intValue() > 1 ? "(" + value.intValue() + " Days)"
						: "(1 Day)";
			}
		});
		SummaryColumnConfig<Number> description = new SummaryColumnConfig<Number>(
				"description", "Description", 80);
		description.setGroupable(false);
		description.setSortable(false);
		List<ColumnConfig> columns = new ArrayList<ColumnConfig>();
		columns.add(id);
		switch (detailedBy) {
		case DAY: {
			columns.add(weekDay);
			columns.add(monthDay);
		}
		case MONTH: {
			columns.add(month);
		}
		case YEAR: {
			columns.add(year);
		}
		}

		columns.add(date);
		columns.add(expensesCount);
		columns.add(expensesAmount);
		columns.add(profitsCount);
		columns.add(profitsAmount);
		columns.add(description);
		return new ColumnModel(columns);
	}

	private void addToolbar() {
		ToolBar topTlb = new ToolBar();
		final Button detailedDateBtn = new Button(
				"Detailed By <b><u>Day</u></b>");
		Menu detailedDateMenu = new Menu();
		CheckMenuItem dayItem = new CheckMenuItem(
				"Detailed By <b><u>Day</u></b>");
		dayItem.addSelectionListener(new SelectionListener<MenuEvent>() {
			@Override
			public void componentSelected(MenuEvent ce) {
				grid.reconfigure(grid.getStore(),
						buildColumnModel(DateHierarhy.DAY));
				detailedDateBtn.setText("Detailed By <b><u>Day</u></b>");
				grid.getStore().getLoader().load();
				// TODO overload reconfigure method and call method
				// fitColumns(false, false, -1); from view object
			}
		});
		dayItem.setChecked(true);
		dayItem.setGroup("dateDetail");
		detailedDateMenu.add(dayItem);
		CheckMenuItem monthItem = new CheckMenuItem(
				"Detailed By <b><u>Month</u></b>");
		monthItem.addSelectionListener(new SelectionListener<MenuEvent>() {
			@Override
			public void componentSelected(MenuEvent ce) {
				grid.reconfigure(grid.getStore(),
						buildColumnModel(DateHierarhy.MONTH));
				detailedDateBtn.setText("Detailed By <b><u>Month</u></b>");
				grid.getStore().getLoader().load();
			}
		});
		monthItem.setGroup("dateDetail");
		detailedDateMenu.add(monthItem);
		CheckMenuItem yearItem = new CheckMenuItem(
				"Detailed By <b><u>Year</u></b>");
		yearItem.addSelectionListener(new SelectionListener<MenuEvent>() {
			@Override
			public void componentSelected(MenuEvent ce) {
				grid.reconfigure(grid.getStore(),
						buildColumnModel(DateHierarhy.YEAR));
				detailedDateBtn.setText("Detailed By <b><u>Year</u></b>");
				grid.getStore().getLoader().load();
			}
		});
		yearItem.setGroup("dateDetail");
		detailedDateMenu.add(yearItem);
		detailedDateBtn.setMenu(detailedDateMenu);
		topTlb.add(detailedDateBtn);
		contentPanel.setTopComponent(topTlb);
	}

	@SuppressWarnings("unchecked")
	@Override
	protected void onLoad() {
		super.onLoad();
		PagingToolBar pagingToolBar = ((PagingToolBar) contentPanel
				.getBottomComponent());
		PagingLoader loader = ((PagingLoader) grid.getStore().getLoader());
		loader.load(0, pagingToolBar.getPageSize());
	}

	public int getRowCount() {
		PagingToolBar pagingToolBar = ((PagingToolBar) contentPanel
				.getBottomComponent());
		return pagingToolBar.getPageSize();
	}

	public void setRowCount(int rowCount) {
		PagingToolBar pagingToolBar = ((PagingToolBar) contentPanel
				.getBottomComponent());
		pagingToolBar.setPageSize(rowCount);
	}

}
